#include "Input.h"
#include <windows.h>
#include "Engine.h"
#include "GameState.h"

/* Input Singleton Functions */ 
Input::Input() 
{
    mDirectInput = NULL;
}
bool Input::Init()
{
    if FAILED(DirectInput8Create(Engine::Instance()->GetInstance(),DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID*) &mDirectInput, NULL) )
    {
        return false;
    }
    mKeyboard.Init();
    mMouse.Init();
    return true;
}
void Input::Process()
{
    mKeyboard.Process();
    mMouse.Process();
}
const Input::Keyboard &Input::GetKeyboard() 
{
    return mKeyboard;
}
const Input::Mouse &Input::GetMouse() 
{
    return mMouse;
}

/* InputDevice Base Class functions */
Input::InputDevice::InputDevice()
{
    m_KeyCount = 0;
    m_pProcessedKeys = NULL;
}
void Input::InputDevice::Shutdown(void)
{
    if(m_DeviceHandle)
    {
        m_DeviceHandle->Unacquire();
        m_DeviceHandle->Release();

        m_DeviceHandle = NULL;

        delete[] m_pProcessedKeys;
    }
}
bool Input::InputDevice::Process(void *pData, int DataSize)
{
    if(!m_DeviceHandle)	return false;

    if(m_DeviceHandle->GetDeviceState( DataSize, pData ))
    {
        m_DeviceHandle->Acquire();

        memset(pData,0,DataSize);

        memset(m_pProcessedKeys,0,sizeof(KeyState)*m_KeyCount);

        return false;
    }

    return true;
}
Input::KeyState Input::InputDevice::GetKey(int KeyID) const
{
    assert(KeyID < m_KeyCount);

    return m_pProcessedKeys[KeyID];
}
int Input::InputDevice::GetKeyCount()	const
{
    return m_KeyCount;
}
void Input::InputDevice::AllocateKeys(int NoOfKeys)
{
    assert(m_pProcessedKeys == NULL);

    m_KeyCount = NoOfKeys;
    m_pProcessedKeys = new KeyState[m_KeyCount];

    for(int iKey=0;iKey < m_KeyCount;iKey++)
    {
        m_pProcessedKeys[iKey] = KeyState_NotPressed;
    }
}
void Input::InputDevice::UpdateKey(bool bPressed, KeyState *pKey)
{
    if(bPressed)
    {
        switch(*pKey)
        {
        case KeyState_NotPressed:
            {
                *pKey = KeyState_Pressed;
            }
            break;

        case KeyState_Pressed:
            {
                *pKey = KeyState_Held;
            }
            break;

        case KeyState_Held:
            {
                *pKey = KeyState_Held;
            }
            break;

        case KeyState_Released:
            {
                *pKey = KeyState_Pressed;
            }
            break;

        default:
            assert(0);
            break;
        }
    }
    else
    {
        switch(*pKey)
        {
        case KeyState_NotPressed:
            {
                *pKey = KeyState_NotPressed;
            }
            break;

        case KeyState_Pressed:
            {
                *pKey = KeyState_Released;
            }
            break;

        case KeyState_Held:
            {
                *pKey = KeyState_Released;
            }
            break;

        case KeyState_Released:
            {
                *pKey = KeyState_NotPressed;
            }
            break;

        default:
            assert(0);
            break;
        }
    }
}

/* Keyboard functions*/
#define MAX_KEYS	(256)
#define DINPUT_KEYPRESS (0x80)
Input::Keyboard::Keyboard()
{
    AllocateKeys(MAX_KEYS);
}
bool Input::Keyboard::Init()
{
    if FAILED(Input::Instance()->GetDInput()->CreateDevice(GUID_SysKeyboard ,&m_DeviceHandle ,NULL)) 						
        return false;
    if FAILED(m_DeviceHandle->SetDataFormat(&c_dfDIKeyboard))															
        return false;
    if FAILED(m_DeviceHandle->SetCooperativeLevel(Engine::Instance()->GetWindow() ,DISCL_NONEXCLUSIVE | DISCL_FOREGROUND)) 			
        return false;

    return true;
}
void Input::Keyboard::Process (void)
{
    unsigned char	RawKeys[MAX_KEYS];

    if(InputDevice::Process(&RawKeys, MAX_KEYS) )
    {
        for(int iKey = 0;iKey < m_KeyCount;iKey++)
        {
            bool t = (RawKeys[iKey]== DINPUT_KEYPRESS);
            UpdateKey(t,&m_pProcessedKeys[iKey]);
            if(t)
            {
                if(iKey < 256)
                {
                        if(m_pProcessedKeys[iKey] == KeyState_Pressed)
                        {
                            Engine::Instance()->GetCurrentState()->NotifyKeyPress(iKey);
                        }
                }
            }
        }
    }
}

/* Mouse Functions */
Input::Mouse::Mouse()
{
    m_position.x = m_position.y = m_oldPosition.x = m_oldPosition.y = 0;
    AllocateKeys(4);
}
bool Input::Mouse::Init()
{
    if FAILED(Input::Instance()->GetDInput()->CreateDevice(GUID_SysMouse ,&m_DeviceHandle ,NULL)) 						
        return false;
    if FAILED(m_DeviceHandle->SetDataFormat(&c_dfDIMouse))															
        return false;
    if FAILED(m_DeviceHandle->SetCooperativeLevel(Engine::Instance()->GetWindow() ,DISCL_NONEXCLUSIVE | DISCL_FOREGROUND)) 			
        return false;

    return true;
}
void Input::Mouse::Process (void)
{
    DIMOUSESTATE dims;

    if(InputDevice::Process(&dims, sizeof(DIMOUSESTATE)) )
    {
        for(int iKey = 0; iKey < m_KeyCount; iKey++)
        {
            UpdateKey(dims.rgbButtons[iKey]?true:false,&m_pProcessedKeys[iKey]);
        }

        D3DXVECTOR2 t = m_position;
        int arr[2] = {0,0};
        Engine::Instance()->GetCurrentMousePosition(arr);
        m_position.x = (float)arr[0]; /**/ m_position.y = (float)arr[1];

        if(t != m_position)
        {
            m_oldPosition = t;
        }
    }
}

/* Helper Functions */
char *WSTR2char(const WCHAR *p)
{
    char *q = new char[wcslen(p)];

    for(int i=0;i<(int)wcslen(p)+1;i++)
    {
        q[i] = (char)p[i];
    }

    return q;
}